package com.hzsparrow.ai.util;

import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.ext.tables.TablesExtension;
import com.vladsch.flexmark.util.data.MutableDataSet;
import lombok.extern.slf4j.Slf4j;

import org.apache.tika.sax.ToHTMLContentHandler;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.ExtractedTextFormatter;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.core.io.FileUrlResource;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文件工具类 - 提供文件上传、读取和内容解析功能
 * 使用Spring AI的TikaDocumentReader来解析不同类型的文档
 */
@Slf4j
public class FileUtils {

    // 默认分块大小
    private static final int DEFAULT_CHUNK_SIZE = 1000;
    private static final int DEFAULT_CHUNK_OVERLAP = 200;

    // 读取文档并转为markdown
    private final CustomReader read2MdReader = path -> {
        try {
            FileUrlResource resource = new FileUrlResource(path);
            TikaDocumentReader reader = new TikaDocumentReader(resource, new ToHTMLContentHandler(), ExtractedTextFormatter.defaults());
            List<Document> documents = reader.get();
            MutableDataSet options = new MutableDataSet();
        
        // 添加表格扩展
        options.set(com.vladsch.flexmark.parser.Parser.EXTENSIONS, Arrays.asList(TablesExtension.create()));
        
        // 配置表格处理选项
        options.set(TablesExtension.COLUMN_SPANS, true)
               .set(TablesExtension.APPEND_MISSING_COLUMNS, true)
               .set(TablesExtension.DISCARD_EXTRA_COLUMNS, false)
               .set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
               .set(TablesExtension.CLASS_NAME, "")
               // 表格格式化选项
               .set(TablesExtension.FORMAT_TABLE_TRIM_CELL_WHITESPACE, true)
               .set(TablesExtension.FORMAT_TABLE_ADJUST_COLUMN_WIDTH, true)
               .set(TablesExtension.FORMAT_TABLE_APPLY_COLUMN_ALIGNMENT, true)
               .set(TablesExtension.FORMAT_TABLE_FILL_MISSING_COLUMNS, true)
               .set(TablesExtension.FORMAT_TABLE_LEAD_TRAIL_PIPES, true)
               .set(TablesExtension.FORMAT_TABLE_SPACE_AROUND_PIPES, true);
            FlexmarkHtmlConverter converter = FlexmarkHtmlConverter.builder(options).build();
        
            List<Document> result = new ArrayList<>(documents.size());
            
            for (Document document : documents) {
                String text = document.getText();
                Document doc = new Document(converter.convert(text), document.getMetadata());
                result.add(doc);
            }
            return result;
        } catch (MalformedURLException e) {
            log.error("无效的文件URL: {}", path, e);
            throw new RuntimeException("无效的文件URL: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("处理文档时出错: {}", path, e);
            throw new RuntimeException("处理文档时出错: " + e.getMessage(), e);
        }
    };

    // 简单文档读取，读取为纯文本
    private final CustomReader simpleReader = path -> {
        try {
            FileUrlResource resource = new FileUrlResource(path);
            TikaDocumentReader reader = new TikaDocumentReader(resource);
            return reader.get();
        } catch (MalformedURLException e) {
            log.error("无效的文件URL: {}", path, e);
            throw new RuntimeException("无效的文件URL: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("处理文档时出错: {}", path, e);
            throw new RuntimeException("处理文档时出错: " + e.getMessage(), e);
        }
    };

    // 使用线程安全的ConcurrentHashMap来存储读取器映射
    private final Map<String, CustomReader> readerMap = new ConcurrentHashMap<>();

    public FileUtils() {
        // 初始化文件类型与读取器的映射
        // 纯文本类型文件
        readerMap.put("txt", simpleReader);
        readerMap.put("md", simpleReader);
        readerMap.put("json", simpleReader);
        readerMap.put("xml", simpleReader);
        readerMap.put("csv", simpleReader);
        
        // 需要转换为Markdown的复杂格式文件
        readerMap.put("xls", read2MdReader);
        readerMap.put("xlsx", read2MdReader);
        readerMap.put("doc", read2MdReader);
        readerMap.put("docx", read2MdReader);
        readerMap.put("pdf", read2MdReader);
        readerMap.put("ppt", read2MdReader);
        readerMap.put("pptx", read2MdReader);
        readerMap.put("html", read2MdReader);
        readerMap.put("htm", read2MdReader);
    }

    /**
     * 上传文件并读取其内容
     * 
     * @param file 上传的文件
     * @param uploadDir 文件上传目录
     * @return 文件内容
     * @throws IOException 如果上传或读取过程中出错
     */
    public String uploadAndRead(MultipartFile file, String uploadDir) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        // 先上传文件
        String relativePath = saveFile(file, uploadDir);
        String absolutePath = Paths.get(uploadDir, relativePath).toString();
        
        // 读取文件内容
        try {
            return read(absolutePath);
        } catch (Exception e) {
            log.error("读取上传文件内容失败: {}", absolutePath, e);
            throw new IOException("读取上传文件内容失败: " + e.getMessage(), e);
        }
    }

    /**
     * 根据文件路径读取文件内容
     * 
     * @param path 文件路径
     * @return 文件内容文本
     * @throws RuntimeException 如果文件不存在或处理出错
     */
    public String read(String path) {
        if (path == null || path.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        
        File file = new File(path);
        
        if (!file.exists()) {
            throw new RuntimeException("文件不存在: " + path);
        }
        
        if (!file.isFile()) {
            throw new RuntimeException("路径不是一个文件: " + path);
        }
        
        String fileName = file.getName();
        int lastDotIndex = fileName.lastIndexOf(".");
        
        if (lastDotIndex == -1) {
            // 没有扩展名，使用默认读取器
            log.info("文件没有扩展名，使用默认读取器处理: {}", path);
            return simpleReader.read(path).get(0).getText();
        }
        
        String suffix = fileName.substring(lastDotIndex + 1).toLowerCase();
        CustomReader reader = readerMap.getOrDefault(suffix, simpleReader);
        
        try {
            List<Document> documents = reader.read(path);
            if (documents == null || documents.isEmpty()) {
                return "未能提取文件内容";
            }
            return documents.get(0).getText();
        } catch (Exception e) {
            log.error("读取文件失败: {}", path, e);
            throw new RuntimeException("读取文件失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 将上传的文件保存到指定目录
     *
     * @param file 上传的文件
     * @param uploadDir 文件保存根目录
     * @return 保存后的文件相对路径
     * @throws IOException 如果保存文件时出错
     */
    public static String saveFile(MultipartFile file, String uploadDir) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        
        if (uploadDir == null || uploadDir.trim().isEmpty()) {
            throw new IllegalArgumentException("上传目录不能为空");
        }
        
        // 按日期创建子目录
        String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        Path directory = Paths.get(uploadDir, datePath);
        
        // 确保目录存在
        Files.createDirectories(directory);
        
        // 安全处理文件名，防止路径遍历攻击
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null) {
            originalFilename = "unnamed_file";
        }
        String fileName = System.currentTimeMillis() + "_" + new File(originalFilename).getName();
        
        Path filePath = directory.resolve(fileName);
        
        // 保存文件
        Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
        
        // 返回相对路径
        return datePath + "/" + fileName;
    }
    
    /**
     * 将HTML内容转换为Markdown格式
     *
     * @param htmlContent HTML内容
     * @return Markdown格式的内容
     */
    public static String convertHtmlToMarkdown(String htmlContent) {
        if (htmlContent == null || htmlContent.trim().isEmpty()) {
            return "";
        }
        
        MutableDataSet options = new MutableDataSet();
        
        // 添加表格扩展
        options.set(com.vladsch.flexmark.parser.Parser.EXTENSIONS, Arrays.asList(TablesExtension.create()));
        
        // 配置表格处理选项
        options.set(TablesExtension.COLUMN_SPANS, true)
               .set(TablesExtension.APPEND_MISSING_COLUMNS, true)
               .set(TablesExtension.DISCARD_EXTRA_COLUMNS, false)
               .set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)
               .set(TablesExtension.CLASS_NAME, "")
               // 表格格式化选项
               .set(TablesExtension.FORMAT_TABLE_TRIM_CELL_WHITESPACE, true)
               .set(TablesExtension.FORMAT_TABLE_ADJUST_COLUMN_WIDTH, true)
               .set(TablesExtension.FORMAT_TABLE_APPLY_COLUMN_ALIGNMENT, true)
               .set(TablesExtension.FORMAT_TABLE_FILL_MISSING_COLUMNS, true)
               .set(TablesExtension.FORMAT_TABLE_LEAD_TRAIL_PIPES, true)
               .set(TablesExtension.FORMAT_TABLE_SPACE_AROUND_PIPES, true);
        
        return FlexmarkHtmlConverter.builder(options).build().convert(htmlContent);
    }
    
    /**
     * 检测文件扩展名
     * 
     * @param fileName 文件名
     * @return 文件扩展名（小写），如果没有扩展名返回空字符串
     */
    public static String getFileExtension(String fileName) {
        if (fileName == null || fileName.isEmpty()) {
            return "";
        }
        
        int lastDotIndex = fileName.lastIndexOf(".");
        if (lastDotIndex == -1 || lastDotIndex == fileName.length() - 1) {
            return "";
        }
        
        return fileName.substring(lastDotIndex + 1).toLowerCase();
    }
    
    /**
     * 自定义读取器接口
     */
    private interface CustomReader {
        List<Document> read(String path);
    }
} 